#include "gprs.h"
#include <Flash.h>
#include "Setup.h"
#include "macros.h"

// ---------- CONSTANTES DE LA CLASE --------------------------

#define CARRIAGE_RETURN_CHAR   0x0D
#define DOUBLE_QUOTATION_CHAR  0X22
#define CTRL_Z_CHAR            0x1A

//const int simPinNumber = ****;

#define NETWORK_REGISTER_TRIALS     15

// #################################################################################################################
//NOTA MUY IMPORTANTE. EL TAMAÑO DEL BUFFER SERIE ESTÁ DEFINIDO EN UNA LIBRERÍA DEL CORE DE ARDUINO. PARA QUE 
//ESTE PROYECTO FUNCIONE BIEN, HE TENIDO QUE CAMBIAR LA LIBRERÍA AUMENTANDO DE 64 A 128 BYTES EL TAMAÑO DEL BUFFER
//PORQUE SINO SE CORTABAN LOS SMS RECIBIDOS Y LOS LEÍDOS DE LA MEMORIA DE LA SIM.
//ESE TAMAÑO PUEDE SER EXCESIVO PARA UN ARDUINO UNO, ASÍ QUE HASTA QUE ENCUENTRE OTRA SOLUCIÓN, LO MEJOR ES COMPILAR
//EL PROYECTO CON BUFFER DE 128 BYTES Y LUEGO DEJARLO EN 64 PARA EL RESTO DE PROYECTOS
#define HARDWARE_SERIAL_BUFFER_SIZE 128 //Sacado de HardwareSerial.cpp en carpeta hardware/arduino/cores/arduino
//##############################################################################################################

// -------------------------------------------------------------------------



//OJO A LA SINTAXIS PARA PASAR LOS PARÁMETROS AL SOFTWARE SERIAL EN EL CONSTRUCTOR. NO ES EVIDENTE.
// VER TUTORIAL EN http://arduinoetcetera.blogspot.com.es/2011/01/classes-within-classes-initialiser.html

Gprs::Gprs(uint8_t switchOnPin, uint8_t statusLedPin)
{
  m_switchOnPin = switchOnPin;
  m_statusLedPin = statusLedPin;
}


//procedimiento para activar el módulo GPRS
boolean Gprs::turnOn()
{
  boolean bRegistered = false;

  DEBUG_PRINT (F("Encendiendo GPRS\n"));

  //TODO: probar a poner aquí el begin del serial1

  //Encendido del módulo (pulso HIGH 2 segundos en el pin 2)
  digitalWrite(m_switchOnPin, HIGH);
  delay(2000);
  digitalWrite(m_switchOnPin, LOW);

  DEBUG_PRINT (F("Esperando registro GPRS en red\n"));  

  for (int i = 0 ; i < NETWORK_REGISTER_TRIALS && !bRegistered; i++)
  {
    //esperamos a que se active el módulo GPRS
    for (int wait = 0; wait < 3; wait++)
      ledBlink(100,100); 

    char reply[32];
    readAtCommandReply("AT+CREG?", reply, 32);

    DEBUG_PRINT(F("Resultado CREG:\n"));
    DEBUG_PRINT(reply);

    if (strstr(reply, "+CREG: 0,1") != NULL)
    {
      //unlockSimCard();
      DEBUG_PRINT(F("Gprs ON\n"));
      bRegistered = true;
    }
    else
      DEBUG_PRINT(F("waiting...\n"));
  }
  if (!bRegistered)
    DEBUG_PRINT(F("NO SE RECIBIO RESPUESTA DE REGISTRO DE LA RED !!\n"));
  return bRegistered;

}

//activación del modo SMS.
//debe hacerse después del encendido del gprs y antes de 
//enviar el primer sms
void Gprs::setSmsTxtMode()
{
  Serial1 << F("AT+CMGF=1\n");
  DEBUG_PRINT(F("SMSs en modo texto\n"));
  delay(500);
}

void Gprs::turnOff()
{
  Serial1 << F("AT*PSCPOF");
  DEBUG_PRINT(F("GPRS off\n"));
}


//Parpadeo con espera (bloquea ejecución)
void Gprs::ledBlink(int on, int off)
{
  digitalWrite(m_statusLedPin, HIGH);
  delay(on);
  digitalWrite(m_statusLedPin, LOW);
  delay (off);
}

boolean Gprs::sendSms(const char* phoneNumber, String msg)
{
  char buffer[140];
  memset(buffer, '\0', sizeof(buffer));
  msg.toCharArray(buffer, sizeof(buffer));
  return sendSms(phoneNumber, buffer);
}

boolean Gprs::sendSms(const char* phoneNumber, const char* msgTxt)
{
  boolean bRes = false;
  //Destello 5 veces indica inicio SMS
  for (int i = 0; i < 5 ; i++)
    ledBlink(100, 100);

  //TODO: por algún motivo, sin este ath + delay (puede que lo que haga efecto sea el delay) no se manda el primer SMS
  Serial1.println("ATH"); 
  DEBUG_PRINT(F("ATH previo a mandar sms"));
  delay(5000);
  Serial1.flush();

  //  COMENTAR EL COMANDO AT PARA EVITAR ENVIO REAL DEL MENSAJE

  Serial1.print("AT+CMGS="); // comando AT para enviar SMS
  Serial1.write(DOUBLE_QUOTATION_CHAR);   // enviar caracter "
  Serial1.print(phoneNumber); // numero de movil receptor del SMS
  Serial1.write(DOUBLE_QUOTATION_CHAR);  //  enviar caracter "
  Serial1.println("");
  delay(1500); 
  Serial1.print(msgTxt); //texto del SMS
  delay(500);
  Serial1.write(CTRL_Z_CHAR); // final del SMS, comando 1A (hex)
  Serial1.println();

  char reply[64];
  readAtCommandReply(reply, 64);

  DEBUG_PRINT(F("SMS reply:\n"));
  DEBUG_PRINT(reply);
  DEBUG_PRINTLN();

  if (strstr(reply, "OK") != NULL)
  {
    DEBUG_PRINT (F("SMS sent OK\n"));
    bRes = true;
  }
  else
  {
    DEBUG_PRINT(F("SMS Not sent\n"));
  }
  delay(5000);
  //Destello 3 veces indica fin SMS
  for (int i = 0; i < 3 ; i++)
    ledBlink(100,100);
  return bRes;
}

String Gprs::readSmsAtIndex(int index)
{
  DEBUG_PRINT(F("Free RAM: "));
  DEBUG_PRINT(String(Helper::availableMemory()) + "\n"); 

  // NOTA: En ocasiones los sms se truncan porque no entran en el buffer,
  // pero los comandos de interés son cortos, así que no importa.
  // No dar valores muy grandes (>200) a este buffer porque andamos muuuy
  // justitos de memoria RAM
  char reply[256];
  readAtCommandReply("AT+CMGR=" + String(index), reply, 256);
  String smsContent = reply;  
  smsContent.replace("\r\n", " ");

  DEBUG_PRINT(F("Free RAM: "));
  DEBUG_PRINT(String(Helper::availableMemory()) + "\n"); 

  return smsContent;
}

boolean Gprs::deleteSmsAtIndex(int index)
{
  boolean bRes = false;
  char data[180];

  readAtCommandReply("AT+CMGD=" + String(index), data, 180);
  String smsContent = data;  
  if (smsContent.indexOf("OK") >= 0)
  {
    bRes = true;
    DEBUG_PRINT(F("sms borrado\n"));
  }
  else
    DEBUG_PRINT(F("Error al borrar sms\n"));
  return bRes;
}


void Gprs::makeCall(const char* phoneNumber)
{
  //Destello 5 veces indica inicio 
  DEBUG_PRINT(F("LLAMANDO AL "));
  DEBUG_PRINT(phoneNumber);
  DEBUG_PRINTLN();

  //TODO: Revisar por qué sin un ATH antes de hacer la primera llamada, el modem no consigue contactar ¿?
  Serial1.println("ATH"); 
  DEBUG_PRINT("ATH previo a la llamada\n");

  Serial1.flush();
  delay(3000);
  for (int i = 0; i < 5 ; i++)
    ledBlink(100, 100);

  String atd = "ATD" + String(phoneNumber) + ";";
  DEBUG_PRINT(atd);

  //OJO, SI EN VEZ DE HACER UN PRINTLN HACEMOS UN PRINT, EL FINAL DE COMANDO ES "\r\n", no solo "\n"
  Serial1.println(atd);


  /*
    char data[64];  
   readAtCommandReply("ATD" + String(phoneNumber) + ";\n", data, 64);
   DEBUG_PRINT(F("Respuesta ATD: \""));
   DEBUG_PRINT(data);
   DEBUG_PRINT("\"\n");
   */
  delay(12000); //Cada tono, 6 seg
  DEBUG_PRINT(F("Colgando llamada\n"));
  Serial1.println("ATH"); //colgaremos y finalizaremos la llamada
  delay(5000); //esperamos para que se libere la línea y el modem esté listo a recibir otro comando AT
  //Destello 3 veces indica fin SMS
  for (int i = 0; i < 3 ; i++)
    ledBlink(100,100);

  Serial1.println("");  
  DEBUG_PRINT(F("FIN LLAMADA\n"));
}

String Gprs::checkReceivedCommand(int index)
{
  String sms = readSmsAtIndex(index);
  DEBUG_PRINT(F("SMS EN POSICION "));
  DEBUG_PRINT(index);
  DEBUG_PRINT(F(": "));
  DEBUG_PRINT(sms);
  DEBUG_PRINTLN();
  int tokenIndex = sms.lastIndexOf(SMS_COMMAND_TOKEN);
  DEBUG_PRINT(F("Token index: "));
  DEBUG_PRINT(String(tokenIndex));
  DEBUG_PRINTLN();
  if (tokenIndex >= 0)
  {
    sms = sms.substring(tokenIndex, sms.length());
    int passwordIndex = sms.lastIndexOf(SMS_COMMAND_PASSWORD);
    Serial << F("Pwd index: ");
    Serial.println(String(passwordIndex));    
    if (passwordIndex == strlen(SMS_COMMAND_TOKEN) + 1)
    {
      sms = sms.substring(passwordIndex + strlen(SMS_COMMAND_PASSWORD) + 1 , sms.length());
      //MENSAJE CORRECTO, SE PUEDE INTERPRETAR EL COMANDO RECIBIDO
      DEBUG_PRINT(F("Recibido comando:-->"));
      DEBUG_PRINT(sms);
      DEBUG_PRINT(F("<--\n"));

      /*
        m_command = sms;
       m_commandIndex = i;
       processCommandCallback();
       m_command = "";
       m_commandIndex = -1;
       */
    }
  }
  else
    sms = "";
  return sms;
}

/*
void unlockSimCard()
 {
 //Enviamos el pin de desbloqueo de la tarjeta
 m_cell.print("AT+CPIN=");
 m_cell.print(simPinNumber);
 m_cell.print(";");
 }
 */

void Gprs::readAtCommandReply(char* reply, int replyLength)
{
  readAtCommandReply("", reply, replyLength);
}

void Gprs::readAtCommandReply(String command, char* reply, int replyLength)
{
  Serial1.println(command);
  Serial1.flush();

  DEBUG_PRINT(command);
  DEBUG_PRINTLN();

  delay(500); //esperamos a la respuesta

  //Limpiamos el buffer antes de escribir nada
  memset(reply, '\0', replyLength);
  int nBytes = Serial1.available(); // Store number of bytes in serial queue

  DEBUG_PRINT(F("Available bytes: "));
  DEBUG_PRINT(nBytes);
  DEBUG_PRINTLN();

  /*
  bool bOverflow = Serial1.overflow();
   if (bOverflow)
   Serial << F("SOFTWARE SERIAL OVERFLOW!!\n");
   */
  int i = 0;
  boolean keepReading = true;

  while (nBytes > 0 && keepReading)
  {
    if (nBytes >= HARDWARE_SERIAL_BUFFER_SIZE) 
      Serial << F("SERIAL 1 OVERFLOW!!\n");

    char c = Serial1.read();
    reply[i] = c;  
    if (i == replyLength - 1)
    {
      keepReading = false;
      DEBUG_PRINT(F("DESTINATION BUFFER IS FULL\n"));
    }
    i++;
    delay(10);
    nBytes = Serial1.available();
  }
}


int Gprs::getSignalStrength()
{
  int strength = -1;
  char data[180];
  readAtCommandReply("AT+CSQ=?", data, 180);
  readAtCommandReply("AT+CSQ", data, 180);  
  if (strstr(data, "OK") != NULL)
  {
    String str = data;
    int tokenIndex1 = str.lastIndexOf(": ");
    DEBUG_PRINT(F("Token index: "));
    DEBUG_PRINT(String(tokenIndex1));
    DEBUG_PRINTLN();
    if (tokenIndex1 >= 0)
    {
      str = str.substring(tokenIndex1 + 2, str.length());
      int tokenIndex2 = str.indexOf(',');
      if (tokenIndex2 >= 0)
      {
        str = str.substring(0, tokenIndex2);
        strength = atoi(str.c_str());
        //El valor 99 significa desconocido o no detectable
        if (strength == 99)
          strength = -1;
        else
          strength = strength * 100 / 32;
        DEBUG_PRINT(F("#######   INTENSIDAD DE COBERTURA GSM: "));
        DEBUG_PRINT(strength);
        DEBUG_PRINTLN();
        
      }
    }    
  }  
  return strength;
}


